#include "log.h"

/*log system singe object*/
static log_priv log_conf;


/* Log levels print symbol styles 
example: logi("hello world!\n") ---- I/logTag: hello world!
*/
const char * log_string[] = {
	"I",
	"D",
	"W",
	"E",
	"",
};

/*****************************************************************************
 * Function      : log_init
 * Description   : log system init function
 * Input         : void  
 * Output        : None
 * Return        : 
 * Others        : 
 * Record
 * 1.Date        : 20180716
 *   Author      : bruce
 *   Modification: Created function

*****************************************************************************/
void log_init(void){
	memset(&log_conf,0,sizeof(log_conf));
}

/*****************************************************************************
 * Function      : log_lock
 * Description   : Lock the log to prevent concurrent access
 * Input         : void  
 * Output        : None
 * Return        : static
 * Others        : 
 * Record
 * 1.Date        : 20180716
 *   Author      : bruce
 *   Modification: Created function

*****************************************************************************/
static int log_lock(void){
	if (log_conf.log_lock){
		log_conf.log_lock(log_conf.log_lock_para);
		return 0;
	}else{
		return -1;
	}
}


/*****************************************************************************
 * Function      : log_unlock
 * Description   : Unlock the log
 * Input         : void  
 * Output        : None
 * Return        : static
 * Others        : 
 * Record
 * 1.Date        : 20180716
 *   Author      : bruce
 *   Modification: Created function

*****************************************************************************/
static int log_unlock(void){
	if (log_conf.log_unlock){
			log_conf.log_unlock(log_conf.log_unlock_para);
			return 0;
	}else{
		return -1;
	}
}


/*****************************************************************************
 * Function      : log_set_global_output_filter
 * Description   : Set global log level output
 * Input         : const int log_level  
 * Output        : None
 * Return        : 
 * Others        : 
 * Record
 * 1.Date        : 20180716
 *   Author      : bruce
 *   Modification: Created function

*****************************************************************************/
int log_set_global_output_filter(const int log_level){
	log_lock();
	if ( log_level <= LOG_NONE_LEVEL ){
		log_conf.global_output_filter = log_level;
		log_unlock();
		return 0;
	}
	log_unlock();
	return -1;
}


/*****************************************************************************
 * Function      : log_set_tag_output_filter
 * Description   : Set a single log level output
 * Input         : const char * tag_name  
                const int log_level    
 * Output        : None
 * Return        : 
 * Others        : 
 * Record
 * 1.Date        : 20180716
 *   Author      : bruce
 *   Modification: Created function

*****************************************************************************/
int log_set_tag_output_filter(const char * tag_name,const int log_level){
	
	log_lock();
	
	unsigned int tag_empty_lable = MAX_TAG_NUM;
	
	for(int i = 0;i < MAX_TAG_NUM; i++){
		
		tag_info * info = &(log_conf.tag_conf[i]);
		
		if ( strlen(log_conf.tag_conf[i].tag_name) == 0 ) {
			if(tag_empty_lable > i){
				tag_empty_lable = i;
			}
			continue;
		}
		
		if (strncmp(info->tag_name,tag_name,store_cmp(tag_name,info->tag_name)) == 0 ){
			info->tag_output_filter = log_level;
			log_unlock();
			return 0;
		}
	}

	if( tag_empty_lable != MAX_TAG_NUM){
		strncpy(log_conf.tag_conf[tag_empty_lable].tag_name,tag_name,store_cmp(tag_name,log_conf.tag_conf[tag_empty_lable].tag_name));
		log_conf.tag_conf[tag_empty_lable].tag_output_filter = log_level;
		log_unlock();
		return 0;
	}
	
	log_unlock();
	return -1;
}


/*****************************************************************************
 * Function      : log_register_output_stream
 * Description   : Register the output interface flow
 * Input         : stream_out output_interface  
 * Output        : None
 * Return        : 
 * Others        : 
 * Record
 * 1.Date        : 20180716
 *   Author      : bruce
 *   Modification: Created function

*****************************************************************************/
int log_register_output_stream( stream_out output_interface ){
	
	log_lock();
	if( output_interface != 0 ){
		log_conf.output_interface = output_interface;
		log_unlock();
		return 0;
	}
	log_unlock();
	return -1;
}

/*****************************************************************************
 * Function      : log_register_clock_callback
 * Description   : Register a mutex interface
 * Input         : get_clock get_clock_callback  
 * Output        : None
 * Return        : 
 * Others        : 
 * Record
 * 1.Date        : 20180716
 *   Author      : bruce
 *   Modification: Created function

*****************************************************************************/
int log_register_clock_callback(get_clock get_clock_callback){
	log_lock();
	if( get_clock_callback != 0 ){
		log_conf.get_clock_callback = get_clock_callback;
		log_unlock();
		return 0;
	}
	log_unlock();
	return -1;
}

/*****************************************************************************
 * Function      : log_set_Global_Display_filter
 * Description   : Set global display content filtering
 * Input         : int global_display_filter  
 * Output        : None
 * Return        : 
 * Others        : 
 * Record
 * 1.Date        : 20180716
 *   Author      : bruce
 *   Modification: Created function

*****************************************************************************/
int log_set_Global_Display_filter(int global_display_filter ){
	log_lock();
	if ( global_display_filter <= LOG_DISP_OFF ){
		log_conf.global_display_filter = global_display_filter;
		log_unlock();
		return 0;
	}
	log_unlock();
	return -1;
}



/*****************************************************************************
 * Function      : log
 * Description   : Log printing and display processing
 * Input         : const LOG_LEVEL log_level  
                const char * file_name     
                const char * fun_name      
                const int line_num         
                const char * name          
                const char * format        
                ...                        
 * Output        : None
 * Return        : 
 * Others        : 
 * Record
 * 1.Date        : 20180716
 *   Author      : bruce
 *   Modification: Created function

*****************************************************************************/
int log(const LOG_LEVEL log_level,const char * file_name,
		const char * fun_name,const int line_num,
		const char * name ,const char * format,...)
{
	char format_buf[128] = {0};
	char log_buf[64];
	memset(format_buf,0,sizeof(format_buf));
	memset(log_buf,0,sizeof(log_buf));
	
	if(log_level > LOG_SYS_ERROR){
		return -1;
	}
	
	log_lock();
	if ( (log_conf.global_output_filter != 0) && (0 == (log_conf.global_output_filter & (1<<log_level))) ){
		log_unlock();
		return -1;
	}
	
	for(int i = 0 ; i < MAX_TAG_NUM ;i++ ){
		tag_info * tag = &log_conf.tag_conf[i];
		if ( !(strncmp(tag->tag_name,name,store_cmp(name,tag->tag_name)))){
			if((tag->tag_output_filter != 0) && ( 0 == (tag->tag_output_filter & (1 << log_level)))){
				log_unlock();
				return -1;
			}
		}	
		
	}

	int len = 0;
	len += snprintf(( char * )(format_buf+len),(sizeof(format_buf) -len),LOG_LEVEL_STYLE,log_string[log_level]);
	//printf("format_buf = %s \n",format_buf);

	if ((0 == log_conf.global_display_filter )||(log_conf.global_display_filter & LOG_FILE_NAME_DISP_ENABLE)){
		//printf("%s\n",file_name);
		len += snprintf(( char * )(format_buf+len),(sizeof(format_buf) -len),LOG_FILE_NAME_STYLE,file_name);
		//printf("format_buf = %s \n",format_buf);
	}

	if ((0 == log_conf.global_display_filter )||(log_conf.global_display_filter & LOG_FUNC_NAME_DISP_ENABLE)){
		//printf("%s\n",fun_name);
		len += snprintf(( char * )(format_buf+len),(sizeof(format_buf) -len),LOG_FUNC_NAME_STYLE,fun_name);
		//printf("format_buf = %s \n",format_buf);
	}
	if ((0 == log_conf.global_display_filter )||(log_conf.global_display_filter & LOG_LINE_NUM_DISP_ENABLE)){
		//printf("%d\n",line_num);
		len += snprintf(( char * )(format_buf+len),(sizeof(format_buf) -len),LOG_LINE_NUM_NAME_STYLE,line_num);
		//printf("format_buf = %s \n",format_buf);
	}
	
	if ((0 == log_conf.global_display_filter )||(log_conf.global_display_filter & LOG_TIME_DISP_ENABLE)){
		//printf("%d\n",line_num);
		if ( log_conf.get_clock_callback){
			int year,month,day,hours,minute,second;
			log_conf.get_clock_callback(&year,&month,&day,&hours,&minute,&second);
			len += snprintf(( char * )(format_buf+len),(sizeof(format_buf) -len),LOG_TIME_STYLE,year,month,day,hours,minute,second);
		}
		//printf("format_buf = %s \n",format_buf);
	}
	len += snprintf(( char * )(format_buf+len),(sizeof(format_buf) -len),LOG_TAG_NAME_STYLE,name);
		//printf("format_buf = %s \n",format_buf);
	
	va_list args;
	/* args point to the first variable parameter */
	va_start(args, format);
	/* You can add your code under here. */
	vsnprintf(log_buf, sizeof(log_buf),format, args);
	va_end(args);
	strncat(format_buf,"%s",strlen("%s"));
	if( log_conf.output_interface != 0 ){
		log_conf.output_interface(format_buf,log_buf);
	}else{
		printf(format_buf,log_buf );
	}
	log_unlock();
}

